from fastapi import FastAPI, Request, HTTPException
from .templates import templates
from fastapi.responses import HTMLResponse, RedirectResponse, FileResponse, JSONResponse
from fastapi.staticfiles import StaticFiles
from fastapi.templating import Jinja2Templates
import os


# 静态文件目录
STATIC_DIR = "/static"
STATIC_FILE_UPLOAD_PATH = "static/pages"
from .routes import api_router, static_pages
from .models import Base, engine, get_db
from .auth import create_initial_user

# 创建数据库表
Base.metadata.create_all(bind=engine)

# 创建FastAPI应用
app = FastAPI(
    title="子鞍的个人网站",
    description="一个基于FastAPI构建的个人门户网站",
    version="1.0.3"
)

# 挂载静态文件
app.mount(STATIC_DIR, StaticFiles(directory="static"), name="static")

# 创建模板引擎实例
app.templates = Jinja2Templates(directory="templates")

# 删除了硬编码路径的路由，使用更通用的嵌套目录路由
# 这样可以支持任意新增的静态页面，而不仅仅是zw/zhiwo目录

# 添加全局404异常处理器，专门处理特定路径的请求
@app.exception_handler(404)
async def custom_404_handler(request: Request, exc: HTTPException):
    """自定义404异常处理器，避免特定路径的404错误日志"""
    from fastapi.responses import Response
    
    # 获取请求路径
    path = request.url.path
    
    # 处理Vite客户端请求
    if path in ["/@vite/client", "/%40vite/client"]:
        print(f"处理Vite客户端请求: {path}")
        return Response(content="// Vite client fallback", media_type="application/javascript")
    
    # 处理静态页面下的常见资源请求
    if path.startswith("/pages/") and any(ext in path for ext in [".css", ".js", ".svg", ".png", ".jpg"]):
        print(f"处理静态页面资源请求: {path}")
        # 对于常见的CSS/JS资源，返回空内容以避免404错误日志
        if path.endswith(".css"):
            return Response(content="/* Fallback CSS */", media_type="text/css")
        elif path.endswith(".js"):
            return Response(content="// Fallback JS", media_type="application/javascript")
        elif path.endswith(".svg"):
            return Response(content="<svg></svg>", media_type="image/svg+xml")
        else:
            return Response(content="", media_type="application/octet-stream")
    
    # 对于其他404错误，返回默认的404响应
    return JSONResponse(status_code=404, content={"detail": "Not Found"})

# 处理所有静态资源请求的路由
@app.get("/pages/{path:path}")
async def serve_all_static_resources(path: str, request: Request):
    """处理所有/pages/下的请求，包括文件和嵌套路径，支持任意静态页面和URL查询参数"""
    # 处理URL查询参数
    if '?' in path:
        path = path.split('?')[0]
    
    # 打印调试信息
    print(f"尝试访问静态资源: {path}")
    
    # 先检查是否是页面目录请求（如 /pages/zw）
    if '/' not in path and '.' not in path:
        try:
            return await serve_static_page(path, request)
        except HTTPException as e:
            if e.status_code != 404:
                raise  # 其他错误直接抛出
    
    # 扩展常见资源目录列表，增加更多可能的静态资源路径
    common_resource_dirs = ['style', 'js', 'icons', 'images', 'css', 'fonts', 'dist', 'static', 'media']
    
    # 优化1: 对于常见资源目录的请求，在所有页面目录中查找
    path_parts = path.split('/')
    if path_parts and path_parts[0] in common_resource_dirs:
        # 在所有页面目录中查找这个资源
        pages_dir = "static/pages"
        if os.path.exists(pages_dir) and os.path.isdir(pages_dir):
            # 先搜索所有页面的顶层目录
            for page_dir in os.listdir(pages_dir):
                page_path = os.path.join(pages_dir, page_dir)
                if os.path.isdir(page_path):
                    direct_path = os.path.join(page_path, path)
                    if os.path.exists(direct_path) and os.path.isfile(direct_path):
                        print(f"在页面目录中找到资源: {direct_path}")
                        return FileResponse(path=direct_path)
                    
                    # 再搜索所有页面的子目录
                    for subdir in os.listdir(page_path):
                        subdir_path = os.path.join(page_path, subdir)
                        if os.path.isdir(subdir_path):
                            potential_path = os.path.join(subdir_path, path)
                            if os.path.exists(potential_path) and os.path.isfile(potential_path):
                                print(f"在页面子目录中找到资源: {potential_path}")
                                return FileResponse(path=potential_path)
    
    # 优化2: 对于带扩展名的文件请求，在所有页面目录中查找
    if '.' in path:
        # 尝试直接在STATIC_FILE_UPLOAD_PATH根目录查找
        direct_root_path = os.path.join(STATIC_FILE_UPLOAD_PATH, path)
        if os.path.exists(direct_root_path) and os.path.isfile(direct_root_path):
            print(f"在静态文件根目录找到文件: {direct_root_path}")
            return FileResponse(path=direct_root_path)
        
        # 尝试在所有页面目录中查找
        pages_dir = "static/pages"
        if os.path.exists(pages_dir) and os.path.isdir(pages_dir):
            for page_dir in os.listdir(pages_dir):
                page_path = os.path.join(pages_dir, page_dir)
                if os.path.isdir(page_path):
                    # 检查直接路径
                    direct_path = os.path.join(page_path, path)
                    if os.path.exists(direct_path) and os.path.isfile(direct_path):
                        print(f"在页面目录中找到文件: {direct_path}")
                        return FileResponse(path=direct_path)
                    
                    # 检查所有子目录
                    for root, dirs, files in os.walk(page_path):
                        # 精确匹配文件名
                        if os.path.basename(path) in files:
                            potential_path = os.path.join(root, os.path.basename(path))
                            print(f"在页面子目录中找到文件: {potential_path}")
                            return FileResponse(path=potential_path)
    
    # 优化3: 对于嵌套路径请求（如 /pages/zw/style.css）
    if '/' in path:
        # 解析目录结构
        parts = path.split('/')
        if len(parts) >= 2:
            # 第一个部分可能是页面目录
            potential_dir = parts[0]
            remaining_path = '/'.join(parts[1:])
            
            # 检查页面状态
            from .models import SessionLocal, StaticPage
            db = SessionLocal()
            try:
                page = db.query(StaticPage).filter(StaticPage.directory == potential_dir).first()
                if page and not page.status:
                    raise HTTPException(status_code=403, detail="页面已被禁用")
            finally:
                db.close()
            
            # 尝试标准路径
            direct_path = os.path.join(STATIC_FILE_UPLOAD_PATH, potential_dir, remaining_path)
            if os.path.exists(direct_path) and os.path.isfile(direct_path):
                return FileResponse(path=direct_path)
            
            # 尝试在子目录中查找
            page_dir = os.path.join(STATIC_FILE_UPLOAD_PATH, potential_dir)
            if os.path.exists(page_dir) and os.path.isdir(page_dir):
                for root, dirs, files in os.walk(page_dir):
                    potential_path = os.path.join(root, remaining_path)
                    if os.path.exists(potential_path) and os.path.isfile(potential_path):
                        print(f"在页面嵌套子目录中找到文件: {potential_path}")
                        return FileResponse(path=potential_path)
    
    # 增强搜索逻辑: 对于常见的CSS/JS文件，扩大搜索范围
    if '.' in path and (path.endswith(('.css', '.js', '.png', '.jpg', '.jpeg', '.gif', '.svg'))):
        # 直接在整个static目录中查找
        static_root = "static"
        if os.path.exists(static_root) and os.path.isdir(static_root):
            for root, dirs, files in os.walk(static_root):
                if os.path.basename(path) in files:
                    potential_path = os.path.join(root, os.path.basename(path))
                    print(f"在static目录中找到文件: {potential_path}")
                    return FileResponse(path=potential_path)
    
    # 所有尝试都失败，返回404
    print(f"静态资源未找到: {path}")
    raise HTTPException(status_code=404, detail=f"文件不存在: {path}")

# 静态页面访问路由 - 移到前面，但添加路径验证以避免匹配文件
@app.get("/pages/{directory}", response_class=HTMLResponse)
async def serve_static_page(directory: str, request: Request):
    """提供静态页面访问"""
    # 避免将带扩展名的请求当作目录处理
    if '.' in directory:
        raise HTTPException(status_code=404, detail="路径错误")
    
    # 首先检查页面状态，只有启用的页面才能访问
    from .models import SessionLocal, StaticPage
    db = SessionLocal()
    try:
        page = db.query(StaticPage).filter(StaticPage.directory == directory).first()
        if page and not page.status:
            # 页面存在但被禁用，返回404页面
            return templates.TemplateResponse("404.html", {"request": request}, status_code=404)
    finally:
        db.close()
    
    # 构建静态页面的HTML文件路径
    index_path = os.path.join(STATIC_FILE_UPLOAD_PATH, directory, "index.html")
    # 减少不必要的日志输出
    
    # 如果直接目录下没有index.html，搜索子目录
    if not os.path.exists(index_path):
        page_dir = os.path.join(STATIC_FILE_UPLOAD_PATH, directory)
        if os.path.exists(page_dir) and os.path.isdir(page_dir):
            # 搜索一级子目录
            for subdir in os.listdir(page_dir):
                subdir_path = os.path.join(page_dir, subdir)
                if os.path.isdir(subdir_path):
                    potential_index = os.path.join(subdir_path, "index.html")
                    if os.path.exists(potential_index):
                        # 找到了子目录中的index.html文件
                        index_path = potential_index
                        break
    
    # 如果index.html存在，返回页面内容
    if os.path.exists(index_path):
        try:
            with open(index_path, "r", encoding="utf-8") as f:
                content = f.read()
            # 文件读取成功
            
            # 检查内容是否为空
            if not content.strip():
                return HTMLResponse(content="<h1>页面内容为空</h1>", media_type="text/html")
            
            # 使用HTMLResponse直接返回内容，并确保设置正确的媒体类型
            return HTMLResponse(content=content, media_type="text/html")
        except Exception as e:
            error_msg = f"读取文件时出错: {str(e)}"
            print(error_msg)
            # 对于错误情况，直接抛出HTTPException而不是返回HTML内容
            raise HTTPException(status_code=500, detail=f"页面加载失败: {str(e)}")
    else:
        # 文件不存在
        # 对于不存在的文件，直接抛出404错误
        raise HTTPException(status_code=404, detail=f"静态页面不存在: {directory}")



# 注册路由
app.include_router(api_router)
app.include_router(static_pages.router)

# 应用启动事件
@app.on_event("startup")
async def startup_event():
    # 确保静态文件目录存在
    os.makedirs(STATIC_FILE_UPLOAD_PATH, exist_ok=True)
    # 创建初始管理员用户
    create_initial_user()
    
    # 初始化定时备份调度器
    try:
        from .config import get_config
        from .utils.scheduler import init_scheduler
        from .models import engine
        import threading
        
        # 获取配置
        config = get_config()
        
        # 获取数据库路径
        db_path = engine.url.database
        
        # 在Docker环境中特殊处理数据库路径
        if not os.path.isabs(db_path):
            # 方法1: 尝试直接使用相对于当前目录的路径
            if os.path.exists(db_path):
                db_path = os.path.abspath(db_path)
            else:
                # 方法2: 使用应用根目录构建绝对路径
                app_root = os.path.dirname(os.path.abspath(__file__))
                candidate_path1 = os.path.join(app_root, "..", db_path)
                
                # 方法3: 尝试Docker容器内的标准路径
                candidate_path2 = "/app/guzian.db"
                
                # 方法4: 尝试当前工作目录下的路径
                candidate_path3 = os.path.join(os.getcwd(), db_path)
                
                # 选择存在的路径
                if os.path.exists(candidate_path1):
                    db_path = candidate_path1
                elif os.path.exists(candidate_path2):
                    db_path = candidate_path2
                elif os.path.exists(candidate_path3):
                    db_path = candidate_path3
                else:
                    # 如果都不存在，使用方法2的路径（最可能正确）
                    db_path = candidate_path1
        
        # 确保数据库文件存在
        if not os.path.exists(db_path):
            # 尝试创建空的数据库文件
            try:
                with open(db_path, 'w') as f:
                    f.write('')
            except Exception as create_error:
                print(f"创建数据库文件失败: {str(create_error)}")
        
        # 确保数据库文件可读可写
        if os.path.exists(db_path):
            try:
                os.chmod(db_path, 0o664)
            except Exception as chmod_error:
                print(f"设置数据库文件权限失败: {str(chmod_error)}")
        
        # 初始化调度器 - 使用单独的线程来避免阻塞启动过程
        def init_scheduler_thread():
            try:
                init_scheduler(config, db_path)
            except Exception as thread_error:
                print(f"调度器线程初始化失败: {str(thread_error)}")
        
        # 创建并启动调度器初始化线程
        init_thread = threading.Thread(target=init_scheduler_thread, daemon=False)
        init_thread.start()
    except Exception as e:
        print(f"初始化定时备份调度器失败: {str(e)}")

# 应用关闭事件
@app.on_event("shutdown")
async def shutdown_event():
    # 关闭定时备份调度器
    try:
        from .utils.scheduler import shutdown_scheduler
        shutdown_scheduler()
    except Exception as e:
        print(f"关闭定时备份调度器时发生错误: {str(e)}")